package com.randi.framework.domain.service.impl;

import com.randi.framework.domain.service.ISysProfileService;
import com.randi.framework.domain.vo.RouterMenu;
import com.randi.framework.domain.vo.Router;
import com.randi.common.utils.TreeUtils;
import com.randi.framework.domain.vo.ProfileInfo;
import com.randi.common.core.domain.vo.SaltInfo;
import com.randi.framework.domain.dto.ProfileUpdateDTO;
import com.randi.common.core.domain.model.LoginUser;
import com.randi.common.core.domain.entity.SysMenu;
import com.randi.common.core.domain.entity.SysUser;
import com.randi.system.domain.params.SysMenuParam;
import com.randi.framework.domain.dto.ProfilePasswordUpdateDTO;
import com.randi.common.enums.MenuTypeEnum;
import com.randi.common.enums.ResultCode;
import com.randi.common.exception.ServiceException;
import com.randi.common.utils.SecurityUtils;
import com.randi.common.utils.StringUtils;
import com.randi.common.utils.secret.SecretUtils;
import com.randi.file.entity.vo.FileUploadVo;
import com.randi.file.service.ISysFileService;
import com.randi.file.service.ISysFileUploadService;
import com.randi.framework.security.service.PasswordService;
import com.randi.framework.domain.service.ISysTokenService;
import com.randi.system.service.ISysMenuService;
import com.randi.system.service.ISysSaltService;
import com.randi.system.service.ISysUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ziye
 */
@Service
public class SysProfileServiceImpl implements ISysProfileService {

    @Resource
    private ISysTokenService iSysTokenService;

    @Resource
    private ISysSaltService iSysSaltService;

    @Resource
    private ISysMenuService iSysMenuService;

    @Resource
    private ISysUserService iSysUserService;

    @Resource
    private ISysFileService iSysFileService;

    @Resource
    private PasswordService passwordService;

    @Resource
    private ISysFileUploadService iSysFileUploadService;

    private static final String USER_AVATAR_PATH = "avatars";

    private static final String PREVIEW_SUFFIX = ".jpg";

    /**
     * 获取路由列表
     *
     * @param user 用户
     * @return 路由列表
     */
    private List<SysMenu> getSysMenuList(SysUser user) {
        SysMenuParam param = new SysMenuParam();
        if (!user.isAdmin()) {
            param.setUserId(user.getUserId());
        }
        param.setStatus(0);
        param.setMenuTypes(Arrays.asList(MenuTypeEnum.CATALOG, MenuTypeEnum.MENU));
        return iSysMenuService.selectSysMenuList(param);
    }

    /**
     * 密码解密处理
     *
     * @param profilePasswordUpdateDto UpdatePassword
     */
    private void handleUpdateDecryptPassword(ProfilePasswordUpdateDTO profilePasswordUpdateDto) {
        if (StringUtils.isEmpty(profilePasswordUpdateDto.getUuid())) {
            return;
        }
        SaltInfo saltInfo = iSysSaltService.getSaltInfo(profilePasswordUpdateDto.getUuid());
        if (saltInfo == null || StringUtils.isEmpty(saltInfo.getSalt())) {
            throw new ServiceException(ResultCode.SECRET_SALT_EXPIRE);
        }
        // 解密
        String original = SecretUtils.decrypt(profilePasswordUpdateDto.getOriginal(), saltInfo.getSalt());
        String current = SecretUtils.decrypt(profilePasswordUpdateDto.getCurrent(), saltInfo.getSalt());
        String confirm = SecretUtils.decrypt(profilePasswordUpdateDto.getConfirm(), saltInfo.getSalt());

        profilePasswordUpdateDto.setOriginal(original);
        profilePasswordUpdateDto.setCurrent(current);
        profilePasswordUpdateDto.setConfirm(confirm);
        // 移除salt
        iSysSaltService.removeSaltInfo(profilePasswordUpdateDto.getUuid());
    }

    /**
     * 更新用户信息
     *
     * @param profileUpdateDto updateUser
     * @param sysUser          SysUser
     */
    private void handleUpdateSysUser(ProfileUpdateDTO profileUpdateDto, SysUser sysUser) {
        Optional.ofNullable(profileUpdateDto.getNickName())
                .filter(StringUtils::isNotEmpty)
                .ifPresent(sysUser::setNickName);
        Optional.ofNullable(profileUpdateDto.getEmail())
                .filter(StringUtils::isNotEmpty)
                .ifPresent(sysUser::setEmail);
        Optional.ofNullable(profileUpdateDto.getPhoneNumber())
                .filter(StringUtils::isNotEmpty)
                .ifPresent(sysUser::setPhoneNumber);
        Optional.ofNullable(profileUpdateDto.getSex())
                .filter(StringUtils::isNotEmpty)
                .ifPresent(sysUser::setSex);
    }

    /**
     * 获取用户信息
     *
     * @return 用户信息
     */
    @Override
    public ProfileInfo getUserInfo() {
        ProfileInfo profileInfo = new ProfileInfo();

        SysUser user = SecurityUtils.getLoginUser().getUser();
        profileInfo.setUser(user);
        Set<String> permissions = SecurityUtils.getLoginUser().getPermissions();
        profileInfo.setPermissions(permissions.toArray(new String[0]));

        // 构建菜单树
        List<SysMenu> sysMenuList = Optional.ofNullable(getSysMenuList(user))
                .orElse(Collections.emptyList());

        if (!sysMenuList.isEmpty()) {
            List<Router> routerList = sysMenuList.stream()
                    .map(Router::new)
                    .collect(Collectors.toList());
            profileInfo.setRouters(TreeUtils.buildTree(routerList, (node, parent) -> {
                if (StringUtils.isNotEmpty(node.getPath())) {
                    node.setPath(parent.getPath() + node.getPath());
                }
            }));

            List<RouterMenu> menuList = sysMenuList.stream()
                    .filter(sysMenu -> sysMenu.getVisible() == 0)
                    .map(RouterMenu::new)
                    .collect(Collectors.toList());
            profileInfo.setMenus(TreeUtils.buildTree(menuList, (node, parent) -> {
                if (StringUtils.isNotEmpty(node.getKey())) {
                    node.setKey(parent.getKey() + node.getKey());
                }
            }));
        }

        return profileInfo;
    }

    /**
     * 更新用户信息
     *
     * @param profileUpdateDto updateUser
     * @return 更新结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateLoginUser(ProfileUpdateDTO profileUpdateDto) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        // 设置登录用户信息参数
        SysUser loginSysUser = loginUser.getUser();
        handleUpdateSysUser(profileUpdateDto, loginSysUser);
        loginUser.setUser(loginSysUser);

        // 设置用户信息参数
        SysUser sysUser = new SysUser();
        sysUser.setUserId(loginUser.getUserId());
        handleUpdateSysUser(profileUpdateDto, sysUser);
        // 更新用户信息和登录用户缓存信息
        int status = iSysUserService.updateSysUser(sysUser);
        if (status != 0) {
            iSysTokenService.updateUserInfo(loginUser);
        }
        return status;
    }

    /**
     * 修改用户密码
     *
     * @param profilePasswordUpdateDto updatePassword
     * @return 结果
     */
    @Override
    public int updateLoginUserPassword(ProfilePasswordUpdateDTO profilePasswordUpdateDto) {
        this.handleUpdateDecryptPassword(profilePasswordUpdateDto);
        // 解密
        String original = profilePasswordUpdateDto.getOriginal();
        String current = profilePasswordUpdateDto.getCurrent();
        String confirm = profilePasswordUpdateDto.getConfirm();

        // 移除salt
        iSysSaltService.removeSaltInfo(profilePasswordUpdateDto.getUuid());
        // 获取用户密码
        SysUser sysUser = iSysUserService.selectSysUserById(SecurityUtils.getUserId());
        if (original.equals(current)) {
            throw new ServiceException(ResultCode.CURRENT_UPDATE_PASSWORD_ERROR_ALIKE);
        } else if (!current.equals(confirm)) {
            throw new ServiceException(ResultCode.CURRENT_UPDATE_PASSWORD_ERROR_NOT_CONFIRM);
        } else if (passwordService.isUpdateLocking()) {
            throw new ServiceException(ResultCode.CURRENT_UPDATE_PASSWORD_ERROR_LOCKING);
        } else if (!passwordService.updateValidatePassword(original, sysUser)) {
            throw new ServiceException(ResultCode.CURRENT_UPDATE_PASSWORD_ERROR_NOT_MATCH);
        }
        // 更新用户密码
        SysUser updateSysUser = new SysUser();
        updateSysUser.setUserId(sysUser.getUserId());
        updateSysUser.setPassword(SecurityUtils.encryptPassword(current));
        return iSysUserService.updateSysUser(updateSysUser);
    }

    /**
     * 更新用户头像
     *
     * @param fileUpload fileUpload
     * @return 状态
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateLoginUserAvatar(FileUploadVo fileUpload) throws IOException {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        // 更新用户信息
        SysUser sysUser = new SysUser();
        sysUser.setUserId(loginUser.getUserId());
        sysUser.setAvatar(fileUpload.getFileHash());
        int status = iSysUserService.updateSysUser(sysUser);
        // 上传图片
        iSysFileUploadService.simpleUploadByFolder(fileUpload, USER_AVATAR_PATH);

        // 更新登录用户信息
        if (status != 0) {
            SysUser user = loginUser.getUser();
            user.setAvatar(fileUpload.getFileHash());
            loginUser.setUser(user);
            iSysTokenService.updateUserInfo(loginUser);
        }
        return status;
    }

    /**
     * 预览头像
     *
     * @param response HttpServletResponse
     * @param fileHash 文件哈希
     */
    @Override
    public void previewCurrentAvatar(HttpServletResponse response, String fileHash) {
        String pathName = USER_AVATAR_PATH + "/" + fileHash;
        String fileName = fileHash + PREVIEW_SUFFIX;
        iSysFileService.filePreview(response, pathName, fileName);
    }

}
